<?php
// +----------------------------------------------------------------------
// | zhanshop-cloud / NginxStats.php    [ 2025/5/7 11:08 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2011~2025 zhangqiquan All rights reserved.
// +----------------------------------------------------------------------
// | Author: zhangqiquan <768617998@qq.com>
// +----------------------------------------------------------------------
declare (strict_types=1);

namespace extend\stats;

use zhanshop\App;
use zhanshop\Helper;
use zhanshop\util\IpLocate;
use zhanshop\util\MaxFile;

class NginxStats
{
    protected $statsLimit = 2000000;
    public function __construct()
    {
        $this->statsLimit = intval(App::env()->get('STATS_NGINX_UNIQUE_LIMIT', '2000000'));
        if($this->statsLimit <= 0){
            $this->statsLimit = 2000000;
        }
    }
    /**
     * 保存分片数据
     * @param string $savePath
     * @param array $logs
     * @return void
     */
    private function saveSlice(string $savePath, array $logs)
    {
        foreach ($logs as $ipMod => $log) {
            $log = implode("\n", $log);
            error_log($log."\n", 3, $savePath.'/'.$ipMod.'.log');
        }
    }

    /**
     * 清理分片
     * @return void
     */
    public function deleteSlice()
    {
        $ipStats = App::runtimePath().'/nginx_ip_stats';
        $uriStats = App::runtimePath().'/nginx_uri_stats';
        Helper::rmdir($ipStats);
        Helper::rmdir($uriStats);
    }

    /**
     * 对日志进行IP和URI分配
     * @param string $accessPath
     * @return void
     */
    public function slice(string $accessPath)
    {
        $this->deleteSlice();
        $ipStats = App::runtimePath().'/nginx_ip_stats';
        Helper::rmdir($ipStats);
        mkdir($ipStats);
        $number = 0;

        $uriStats = App::runtimePath().'/nginx_uri_stats';
        Helper::rmdir($uriStats);
        mkdir($uriStats);
        $ipLogs = [];
        $uriLogs = [];
        $totalLine = 0;
        foreach (App::make(MaxFile::class)->readLines($accessPath) as $v){
            if ($v){
                $arr = json_decode($v,true,512, JSON_INVALID_UTF8_IGNORE);
                if($arr){
                    $totalLine++;
                    $uri = $arr['host'].$arr['uri'];
                    $remoteAddr = $arr['remote_addr'];
                    $ipMod = Helper::ipMod($remoteAddr);
                    $uriMod = Helper::strMod($uri);
                    $uriLogs[$uriMod][] = $uri;
                    $ipLogs[$ipMod][] = $remoteAddr;
                    $number ++;
                    if($number >= 200000){
                        $number = 0;
                        $this->saveSlice($ipStats, $ipLogs);
                        $this->saveSlice($uriStats, $uriLogs);
                        $ipLogs = [];
                        $uriLogs = [];
                    }
                }
            }
        }
        if($ipLogs) $this->saveSlice($ipStats, $ipLogs);
        if($uriLogs) $this->saveSlice($uriStats, $uriLogs);
        return [
            'ip' => glob($ipStats.DIRECTORY_SEPARATOR. '*.log'),
            'uri' => glob($uriStats.DIRECTORY_SEPARATOR. '*.log'),
            'total_line' => $totalLine,
        ];
    }

    /**
     * ip分片地区统计 包含各个省份的IP数，总的IP数量，总的访问数
     * @param array $paths
     * @return array
     */
    public function ipSliceAreaStats(array $paths)
    {
        $ipLocate = App::make(IpLocate::class);

        $ipArea = [];
        $totalUv = 0;
        $totalPv = 0;
        foreach ($paths as $path){
            $ips = [];
            foreach (App::make(MaxFile::class)->readLines($path) as $ip){
                $ip = str_replace(["\r", "\n"],'', $ip);
                $ips[$ip] = $ips[$ip] ?? 0;
                $ips[$ip] ++;
                $totalPv++;
                if(count($ips) >= $this->statsLimit) break; // 避免内存溢出最多统计超出不统计
            }
            $totalUv += count($ips);
            foreach ($ips as $ip => $number){
                $location = $ipLocate->getLocation($ip);
                $province = $location['province'] ?? '';
                if($province == false) $province = '未知';
                $ipArea[$province] = $number;
            }
        }
        return [
            'uv_list' => $ipArea,
            'total_uv' => $totalUv,
            'total_pv' => $totalPv,
        ];
    }

    /**
     * 获取IP分片统计获取top1000
     * @param array $paths
     * @return array
     */
    public function ipSliceTop1000Stats(array $paths)
    {
        $ipLocate = App::make(IpLocate::class);
        $top1000s = [];
        foreach ($paths as $path){
            $ips = [];
            foreach (App::make(MaxFile::class)->readLines($path) as $ip){
                $ip = str_replace(["\r", "\n"],'', $ip);
                $ips[$ip] = $ips[$ip] ?? 0;
                $ips[$ip] ++;
                if(count($ips) >= $this->statsLimit) break; // 避免内存溢出最多统计超出不统计
            }
            arsort($ips);
            $top1000 = [];
            foreach ($ips as $ip => $number){
                $top1000[$ip] = $number;
                if(count($top1000) >= 1000) break;
            }
            foreach ($top1000 as $ip => $number){
                $top1000s[$ip] = $number;
            }
        }
        arsort($top1000s);
        $top1000 = [];
        // 最后再取1000条
        foreach ($top1000s as $ip => $number){
            $location = $ipLocate->getLocation($ip);
            $area = $location['area'] ?? ($ip.':未知归属地');
            $top1000[$ip.'&nbsp;'.$area] = $number;
            if(count($top1000) >= 1000) break;
        }
        arsort($top1000);
        return $top1000;
    }
    /**
     * 获取uri分片统计获取top1000
     * @param array $paths
     * @return array
     */
    public function uriSliceTop1000Stats(array $paths)
    {
        $top1000s = [];
        foreach ($paths as $path){
            $uris = [];
            foreach (App::make(MaxFile::class)->readLines($path) as $uri){
                $uri = str_replace(["\r", "\n"],'', $uri);
                $uri = substr($uri, 0, 128); // 截取一下url 避免因url过长而塞满内存
                $uris[$uri] = $uris[$uri] ?? 0;
                $uris[$uri] ++;
                if(count($uris) >= $this->statsLimit) break;
            }
            arsort($uris);
            $top1000 = [];
            foreach ($uris as $uri => $number){
                $top1000[$uri] = $number;
                if(count($top1000) >= 1000) break;
            }
            foreach ($top1000 as $uri => $number){
                $top1000s[$uri] = $number;
            }
        }
        arsort($top1000s);
        $top1000 = [];
        // 最后再取1000条
        foreach ($top1000s as $uri => $number){
            $top1000[$uri] = $number;
            if(count($top1000) >= 1000) break;
        }
        return $top1000;
    }

}